package batchjob;

import java.util.Enumeration;

/** Defines a 2D table useful for recording data. */
public class SequenceTable implements ConvertToText
{

    /** Contains a number of SequenceTableColumn's. 
	First one is the identity column, the rest are data. */
    public RKUVector datasequences;

    /** True if the identity column is an autoincrement column. */
    public boolean autoincrement;

    /** The startvalue for the identity. (Only used if autoincrement is true)
     */
    public double startvalue;

    /** The stepsize for the identity. (Only used if autoincrement is true)
     */
    public double stepsize;

    /** The current value of the identity. */
    public double curvalue;

    /** Is this the first reading of the curvalue? */
    boolean firstreading;

    /** The base filename is the first part of the filename when the data is
	stored by the BatchJobScheduler. */
    public String basefilename;

    /** The number of recorded rows in this SequenceTable. */
    public int numberofrows;

    /** Create a new SequenceTable. You have to create a SequenceTableColumn 
	and set it with setIdentityColumn(). */
    public SequenceTable(String abasefilename)
    {
	super();
	datasequences = new RKUVector();
	autoincrement = false;
	numberofrows = 0;
	basefilename = abasefilename;
    }

    /** Create a new SequenceTable. This will set autoincrement to true and
	create the necessary identity column. */
    public SequenceTable(String abasefilename, double astartvalue, double astepsize)
    {
	super();
	datasequences = new RKUVector();

	autoincrement = true;
	stepsize = astepsize;
	startvalue = astartvalue;
	curvalue = startvalue;
	firstreading = true;
	numberofrows = 0;
	basefilename = abasefilename;

	datasequences.addElement(new SequenceTableColumn("ID"));
    }

    /** Set the identity column. */
    public void setIdentityColumn(SequenceTableColumn idcolumn)
    {
	if (datasequences.size()>0) {
	    datasequences.setElementAt(idcolumn,0);
	}
	else {
	    datasequences.addElement(idcolumn);
	}
    }


    /** Add a column to this table. */
    public void addColumn(SequenceTableColumn column)
    {
	// fill the column with nulls if to short
	while (column.size()<numberofrows) {
	    column.recordData(null);
	};

	datasequences.addElement(column);
    }

    /** Add a number of columns to this table. */
    public void addColumns(SequenceTableColumn[] columns)
    {

	for (int i=0;i<columns.length;i++) {
	    addColumn(columns[i]);
	}
    }

    /** Record a row of data. If you don't use automatic increasing of 
	the identity column then the first entry in the array must be
	the id-value. If you want to skip fields put a null in those you
	want to skip.
    */
    public void recordData(Object[] thedata)
    {
	int i, j;
	SequenceTableColumn curseq;

	i = 0;
	j = 0;

	// Set the identity col
	if (!autoincrement) {
	    curseq = (SequenceTableColumn)datasequences.elementAt(i);
	    curseq.recordData(thedata[j]);
	    j++;
	}
	else {
	    if (firstreading) {
		firstreading = false;
	    }
	    else {
		curvalue += stepsize;
	    };
	    curseq = (SequenceTableColumn)datasequences.elementAt(i);
	    curseq.recordData(new Double(curvalue));
	};
	i++;

	for (;j<thedata.length;j++) {
	    curseq = (SequenceTableColumn)datasequences.elementAt(i);
	    curseq.recordData(thedata[j]);
	}

	numberofrows++;
    }

    /** Add and initialize an empty row to the table. 
	This can be used in connection with the updateLastRow methods. */
    public void initializeRow()
    {
	int i;
	SequenceTableColumn curseq;

	i = 0;

	// Set the identity col
	if (autoincrement) {
	    if (firstreading) {
		firstreading = false;
	    }
	    else {
		curvalue += stepsize;
	    };
	    curseq = (SequenceTableColumn)datasequences.elementAt(i);
	    curseq.recordData(new Double(curvalue));
	    i++;
	};


	while(i<datasequences.size()) {
	    curseq = (SequenceTableColumn)datasequences.elementAt(i);
	    curseq.recordData(null);
	    i++;
	}

	numberofrows++;
    }

    /** Update an entry in the last row of the table. 
	@param index The index to update. (0 is the identity.)
	@param data The object to insert at <tt>index</tt> in the last row.
    */
    public void updateLastRow(int index, Object data)
    {
	SequenceTableColumn curseq;

	curseq = (SequenceTableColumn)datasequences.elementAt(index);
	curseq.updateLastRow(data);
    }

    /** Update an entry in the last row of the table. 
	@param index The index to update. (0 is the identity.)
	@param data The double to insert at <tt>index</tt> in the last row.
	It will be wrapped in a Double object.
    */
    public void updateLastRow(int index, double data)
    {
	SequenceTableColumn curseq;

	curseq = (SequenceTableColumn)datasequences.elementAt(index);
	curseq.updateLastRow(new Double(data));
    }

    /** Update an entry in the last row of the table. 
	@param index The index to update. (0 is the identity.)
	@param data The integer to insert at <tt>index</tt> in the last row.
	It will be wrapped in a Integer object.
    */
    public void updateLastRow(int index, int data)
    {
	SequenceTableColumn curseq;

	curseq = (SequenceTableColumn)datasequences.elementAt(index);
	curseq.updateLastRow(new Integer(data));
    }

    /** Update an number of entries in the last row of the table. 
	@param index The index to start at. (0 is the identity.)
	@param data The objects to insert from <tt>index</tt> and on.
    */
    public void updateLastRow(int index, Object[] data)
    {
	SequenceTableColumn curseq;

	for (int i=0;i<data.length;i++) {
	    curseq = (SequenceTableColumn)datasequences.elementAt(index+i);
	    curseq.updateLastRow(data[i]);
	}
    }

    /** Update an number of entries in the last row of the table. 
	@param index The index to start at. (0 is the identity.)
	@param data The doubles to insert from <tt>index</tt> and on.
	They will be wrapped in Double objects.
    */
    public void updateLastRow(int index, double[] data)
    {
	SequenceTableColumn curseq;

	for (int i=0;i<data.length;i++) {
	    curseq = (SequenceTableColumn)datasequences.elementAt(index+i);
	    curseq.updateLastRow(new Double(data[i]));
	}
    }

    /** Update an number of entries in the last row of the table. 
	@param index The index to start at. (0 is the identity.)
	@param data The integers to insert from <tt>index</tt> and on.
	They will be wrapped in Integer objects.
    */
    public void updateLastRow(int index, int[] data)
    {
	SequenceTableColumn curseq;

	for (int i=0;i<data.length;i++) {
	    curseq = (SequenceTableColumn)datasequences.elementAt(index+i);
	    curseq.updateLastRow(new Integer(data[i]));
	}
    }

    /** Update an entry in the last row of the table. 
	@param name The name of the column to update.
	@param data The object to insert in the column in the last row.
    */
    public void updateLastRow(String name, Object data)
    {
	SequenceTableColumn curseq;
	Enumeration ennum;

	for (ennum = datasequences.elements();ennum.hasMoreElements();) {
	    curseq = (SequenceTableColumn)ennum.nextElement();
	    if (name.equals(curseq.getName())) {
		curseq.updateLastRow(data);
		return;
	    }
	}
	System.out.println("WARNING: SequenceTable.updateLastRow()\nCould not find column with name= \""+name+"\"");
    }

    /** Clear the recorded data. This method remove the data from all columns.
     */
    public void clearData()
    {
	for(Enumeration ennum= datasequences.elements();ennum.hasMoreElements();) {
	    ((SequenceTableColumn)ennum.nextElement()).clearData();
	}

	// Reset auto ID
	curvalue = startvalue;
	firstreading = true;
	numberofrows = 0;
    }

    /** Get a column. */
    public SequenceTableColumn getColumn(int i)
    {
	return (SequenceTableColumn)datasequences.elementAt(i);
    };


    /** Get the basefilename. */
    public String getBasefilename()
    {
	return basefilename;
    }

    /** Convert the data to a delimited textstring. */
    public String convertDataToDelimited(String columndelim, String rowdelim, String prependtorow)
    {
	StringBuffer res = new StringBuffer();
	int i, j;
	int numberofrows, numberofcols;

	// Print data

	numberofrows = ((SequenceTableColumn)datasequences.elementAt(0)).size();
	numberofcols = datasequences.size();
	for (i=0; i<numberofrows;i++) {
	    res.append(prependtorow);
	    for (j=0; j<numberofcols;j++) {
		res.append(((SequenceTableColumn)datasequences.elementAt(j)).getReadingAsText(i));
		if (!(j == numberofcols-1)) {
		    res.append(columndelim);
		}
		else {
		    res.append(rowdelim);
		};
	    };
	};

	return res.toString();
    };

    /** Convert the headings to a delimited textstring. */
    public String convertHeadingsToDelimited(String columndelim, String rowdelim, String prependtorow)
    {
	StringBuffer res = new StringBuffer();
	int i, j;
	int numberofrows, numberofcols;

	// Print names
	res.append(prependtorow);
	for (i=0; i<datasequences.size();i++) {
	    res.append(((SequenceTableColumn)datasequences.elementAt(i)).getName());
	    if (!(i == datasequences.size()-1)) {
		res.append(columndelim);
	    }
	    else {
		res.append(rowdelim);
	    }
	}
	return res.toString();
    }

    /** Convert the data to a textstring according to the GNUPlot format. */
    public String convertToGNUPlot()
    {
	return "";
    };
}
